﻿using System;

namespace Maria.Common.Aspects
{
    /// <summary>
    /// Базовый класс для атрибутов, навешиваемых на методы (или сразу на целый класс)
    /// для перехвата вызова методов
    /// </summary>
    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Struct)]
    public class GenericMethodWrapper : WrapperParameter
    {
        /// <summary>
        /// Сигнатура авто-сгенерированного вызывателя оригинального метода
        /// </summary>
        public delegate object InvokeOriginal(object[] MethodParameters);

        /// <summary>
        /// Обёртка вокруг перехваченного метода.
        /// Наследники должны перекрывать эту функцию (используя keyword 'new', чтобы не допустить ошибки в сигнатуре метода)
        /// </summary>
        /// <param name="AttributeParameters">Все сконструированные атрибуты-параметры (если наследник GenericMethodWrapper имеет конструктор с параметрами, то и он тоже)</param>
        /// <param name="MethodParameters">Параметры перехваченного метода</param>
        /// <param name="Invoker">Лямбда Func<object[] MethodParameters, object Result> для вызова оригинального метода</param>
        /// <param name="WrappedInstance">Экземпляр класса, метод которого вызван или null, если метод static</param>
        /// <param name="WrappedType">Токен класса, содержащего метод. Тип класса можно получить так: Type t = Type.GetTypeFromHandle(WrappedType)</param>
        /// <param name="WrappedMethod">Название перехваченного метода</param>
        /// <returns>Результат вызова перехваченного метода</returns>
        public static object OnInvoke(WrapperParameter[] AttributeParameters, object[] MethodParameters, InvokeOriginal Invoker, object WrappedInstance, RuntimeTypeHandle WrappedType, string WrappedMethod)
        {
            return Invoker(MethodParameters);
        }
    }

    /// <summary>
    /// Базовый класс для параметра врапера
    /// </summary>
    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Struct)]
    public class WrapperParameter : Attribute
    {
    }

    /// <summary>
    /// Помечает атрибуты - наследников WrapperParameter, чтобы было понятно,
    /// к какому наследнику GenericMethodWrapper они относятся
    /// </summary>
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
    public class LinkToWrapperAttribute : Attribute
    {
        public LinkToWrapperAttribute(Type wrapperType) { }
    }

    /// <summary>
    /// При навешивании GenericMethodWrapper на весь класс
    /// позволяет выборочно избежать перехвата методов, на которых висит этот атрибут.
    /// Должен быть прикручен к определённому типу Wrapper-а, через LinkToWrapper
    /// </summary>
    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Struct)]
    public class NoWrapAttribute : WrapperParameter
    {
    }
}
